package com.cwl.controller;

import com.cwl.constant.FileConstant;
import com.cwl.properties.FileProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;

@RestController
@Slf4j
public class DownloadController {
    FileProperties fileProperties;

    public DownloadController(FileProperties fileProperties) {
        this.fileProperties = fileProperties;
    }

    @GetMapping("/download")
    public void downLoadFile(HttpServletRequest request, HttpServletResponse response) {
        File file = new File("F:\\video\\解压后\\挺丁而出做黄油！4\\Subtitled Version 挺丁而出作黄油!4 ～实力检测篇.mp4");
        response.setCharacterEncoding(FileConstant.UTF8);
        try (/*BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));*/
                RandomAccessFile raf = new RandomAccessFile(file, "r");
                BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream())) {
            //分片下载
            long length = file.length();
            response.setContentType("application/x-download");//设置媒体类型为下载类型，告诉前端你要下载
            String fileName = URLEncoder.encode(file.getName(), FileConstant.UTF8);//下载的文件极有可能是一个中文，需要编码为utf8避免乱码
            response.addHeader("Content-Disposition", "attachment;filename=" + fileName);//告诉前端弹出一个另存为的对话框，后者告诉另存为文件名默认的位置
            //前端会传一个Range的Head参数过来表示要做分片下载
            response.setHeader("Accept-Range", "bytes");//后端需要兼容就得告诉前端到底支不支持长连接取分片下载,并说明单位
            response.setHeader("fSize", String.valueOf(length));//自定义Head头，表示文件长度
            response.setHeader("fName", fileName);

            long pos = 0, last = length - 1, sum = 0;//起始位置、结束位置和目前读取的总大小
            if (null != request.getHeader("Range")) {//看前端是否需要分片下载，需要才做分片
                //TODO 实际应用中如果Range不是一个数字而是一个字符串怎么办？
                response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);//需要设置一个响应码，因为分片下载正常使用不会返回200的，因为200就表示请求已经结束了，所以要告诉浏览器端，我是一个分片断点 的下载
                //获取分片信息,因为Range格式是 bytes=0-100 或者 bytes=100-1000这样的，所以需要把它拆开我们才知道应该去读哪一段
                String range = request.getHeader("Range").replaceAll("bytes=", "");
                String[] strRange = range.split("-");
                if (strRange.length == 2) {//因为还有一个状态是 bytes=100-
                    pos = Long.parseLong(strRange[0].trim());
                    last = Long.parseLong(strRange[1].trim());
                    /*
                     * 假设分片设置为1024字节,那么客户端方传过来区间一定是[0-1024-1],[1024,2048-1]这样的，最后一个分片也一定是[1024*n,1024*n+1024-1]
                     * 1.所以最后一个分片过来时，右区间last是大于等于length-1的
                     * 2.当判断出右区间last是大于等于length-1时，就是最后一个分片
                     * 3.这时last是分片长度的整数倍-1，大于等于length-1
                     * 4.所以将last有区间设为文件长度length-1即可
                     */
                    if (last > length - 1) {
                        last = length - 1;
                    }
                } else {//这种情况说明是 100- 这种情况 ,就从起点取到结尾即可
                    pos = Long.parseLong(range.replaceAll("-", "").trim());
                }
            }
            long rangeLength = last - pos + 1;//实际读多少字节数
            //告诉客户端当前读取了多少内容以及读的是哪个分片
            String contentRange = "bytes " + pos + "-" + last + "/" + length;
            response.setHeader("Content-Range", contentRange);
            response.setHeader("Content-Length", String.valueOf(rangeLength));
            //利用InputStream skip 方法跳过前面的字节跳着读取，通过RandomAccessFile也可以实现同样的效果
//            bis.skip(pos);
            raf.seek(pos);
            //文件读写常规流程
            byte[] buffer = new byte[1024];//定义缓冲区
            int len;//定义长度
            while (sum < rangeLength) {
                int result = (int) (rangeLength - sum);
                if (result >= buffer.length) {
                    len = raf.read(buffer);
                } else {
                    len = raf.read(buffer, 0, result);
                }
                bos.write(buffer, 0, len);
                sum += len;
            }
            log.debug("【服务端】分片{}写入响应体", pos / fileProperties.getPerPage());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
